package com.itheima.spring.aop;

import com.itheima.spring.aop.advisor.Advisor;
import com.itheima.spring.aop.advisor.AdvisorRegistry;
import com.itheima.spring.aop.advisor.PointcutAdvisor;
import com.itheima.spring.aop.aware.BeanFactoryAware;
import com.itheima.spring.aop.pointcut.Pointcut;
import com.itheima.spring.beans.BeanFactory;
import com.itheima.spring.beans.BeanPostProcessor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.*;

/**
 * AOP增强处理的观察者实现
 */
public class AdvisorAutoProxyCreator implements AdvisorRegistry, BeanFactoryAware, BeanPostProcessor {

    private List<Advisor> advisors;

    private BeanFactory beanFactory;


    public AdvisorAutoProxyCreator() {
        this.advisors = new ArrayList<>();
    }

    @Override
    public void registryAdvisor(Advisor advisor) {
        this.advisors.add(advisor);
    }

    @Override
    public List<Advisor> getAdvisors() {
        return advisors;
    }

    @Override
    public void setBeanFactory(BeanFactory bf) {
        this.beanFactory = bf;
    }

    /**
     * 后置增强
     * @param bean
     * @param beanName
     * @return
     * @throws Throwable
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

        // 在此判断bean是否需要进行切面增强
        List<Advisor> matchedAdvisors = getMatchedAdvisors(bean, beanName);
        // 如需要就进行增强,再返回增强的对象
        if(CollectionUtils.isNotEmpty(matchedAdvisors)){
            bean = this.createProxy(bean,beanName,matchedAdvisors);
        }

        return bean;
    }

    /**
     * 在此判断bean是否需要进行切面增强
     * @param bean
     * @param beanName
     * @return
     */
    private List<Advisor> getMatchedAdvisors(Object bean, String beanName) {

        if(CollectionUtils.isEmpty(advisors)){
            return null;
        }
        
        //得到类和类的所有方法
        Class<?> beanClass = bean.getClass();
        List<Method> allMethods = this.getAllMethodForClass(beanClass);
        // 存放匹配的Advisor的list
        List<Advisor> matchAdvisors  = new ArrayList<>();
        // 遍历Advisor来找匹配的
        for (Advisor advisor:this.advisors){
            if(advisor instanceof PointcutAdvisor){
                if(isPointcutMatchBean((PointcutAdvisor) advisor,beanClass,allMethods)){
                    matchAdvisors.add(advisor);
                }

            }
        }
        return matchAdvisors;
    }

    //判断类及类的方法是否和切面匹配
    public boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods){

        Pointcut pointCut = pa.getPointCut();

        // 首先判断类是否匹配
        if(!pointCut.matchsClass(beanClass)){
            return false;
        }
        // 再判断是否有方法匹配
       for (Method method: methods){
           if(!pointCut.matchsMethod(method,beanClass)){
               return true;
           }
       }


        return false;
    }

    /**
     * 获取类的所有方法,包括继承的父类和实现的接口里面的方法
     * @param beanClass
     * @return
     */
    private List<Method> getAllMethodForClass(Class<?> beanClass) {

        List<Method> allMethods = new LinkedList<>();
        //获取beanClass的所有接口
        Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfaces(beanClass));
        classes.add(beanClass);

        //遍历所有的类和接口反射获取到所有的方法
        for (Class<?> clazz:classes){
            Method[] declaredMethods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method:declaredMethods){
                allMethods.add(method);
            }
        }
        return allMethods;
    }

    // 创建代理对象增强
    private Object createProxy(Object bean,String beanName,List<Advisor> matchAdvisors) throws Throwable{

        // 通过AopProxyFactory工厂去完成选择、和创建代理对象的工作
        return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean,beanName,matchAdvisors,beanFactory).getProxy();
    }
}
